查看原文
其他

c++实现的一种代码膨胀变形壳

2018-01-10 淡淡的荧光 看雪学院


主要实现3个功能


1.代码膨胀

2.代码变形

3.对地址常量包括字符串常量进行加密



代码膨胀



代码膨胀无非就是指令等价替换,不过这里没条指令最好能有多个膨胀规则,这样会加大还原难度。


原始程序大小为6kb,经过5次混淆后,大小变为66kb。



混淆前代码:



混淆后代码:



从上图会发现有大量的垃圾代码生成。



代码变形



1. 有分支跳转改成无分支跳转。


在逆向分析时主要是通过条件跳转来确定函数的流程,根据跳转的目标地址来确定这是一个分支还是一个循环。如果将所有条件跳转改成无分支跳转,那么程序只有在动态执行时,才能确定流程。这大大提高了逆向分析的难度。


另外,ida在进行还原伪c代码时,遇到无分支跳转,会进行截断,导致后面指令无法还原伪c代码。这对靠f5吃饭的人说就gameover了。因为此时已经控制了代码流程, 如果在后面加一些垃圾指令,会导致后面的代码全部识别错乱,只有运行时才确定要跳到哪个位置。


另外对 jmp imm, call imm和call [imm](一般这种间接call都是api调用) 也改成无分支跳转。


本来没打算对这类指令进行处理,但是发现效果还挺好,干脆就加上吧。因为jmp imm也可以作为判断分支或者循环的依据,而call也是一个比较关键的指令,尤其是call api,在逆向分析时,通过观察api的前后代码来确定代码到底在干嘛。


这样一处理,你静态分析时看不到任何条件跳转,看不到任何call调用(除了call reg,当然了,静态分析你也看不出来call reg)。


另外对条件跳转和call这类指令的下一条指令加一些垃圾代码,会导致后面的整段指令识别错误。也会给别人留下个坑,比如一般人想步过这个函数调用,会在函数的下一条指令地址处下断点,但这条指令实际上永远不会执行。


实现思路


1. 将跳转目标地址保存到一个地址数组表里面,在建一个索引表记录地址表的索引,在对索引进行随机加密。


运行时根据条件码得到正确的索引表地址,在对索引进行解密,得到正确的目标地址偏移。


代码变形前:



代码变形后:



两幅图对比发现call memset 变成了一大串指令,最后以ret结尾。这一大串做了一个事情,去索引表找到地址表的索引,在根据这个索引去地址表找到正确偏移值,加上基地址,得到目标地址。


同时会把返回地址压栈,实际上返回地址是可以随意更改的,我是按照代码块进行处理的,随意加一些垃圾指令, 把后面的代码块往下移动一点就好了,这个自己改,另外索引加密这个我注释掉了,去掉就可以。


大家发现这里先push了2个寄存器,在这里偷了点懒,实际上最好应该是用反汇编引擎扫一下当前哪些寄存器是被占用的,再从没被占用的寄存器中随机分配2个寄存器。如果没有可利用的寄存器,在这么做。


call memset是一个立即数,再来看下scanf (call [imm])


代码变形前:



代码变形后:



再来看下f5后的代码:


代码变形前:



代码变形后:



从上图可以发现变形后,ida根本就没有识别完整个函数,只识别了一点点。ida遇到无条件跳转会截断当前函数。


对地址常量包括字符串常量进行加密 :


在进行逆向分析时,很多人第一反应会去看看有没有一些敏感字符串,根据ida的交叉引用来确定目标地址。


另外一些地址值可能也比较关键,比如回调函数。而这类常量赋值一般都是mov指令和push指令,对 mov imm,push imm这类指令的立即数做随机加密,就可以起到一个保护的作用。


加密前:



加密后:






本文由看雪论坛 淡淡的荧光 原创

转载请注明来自看雪社区



热门阅读

点击阅读原文/read,

更多干货等着你~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存